package simplifiedPastry.simulation;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.StringTokenizer;
import simplifiedPastry.Message;
import simplifiedPastry.NodeId;
import simplifiedPastry.Pair;
import simplifiedPastry.exceptions.NodeConnectionException;
import simplifiedPastry.exceptions.NodeStateException;
import simplifiedPastry.exceptions.ServerRemoveCountException;
import simplifiedPastry.simulation.RegistratorData;

public class ClientImpl {
	public static void main(String[] args) {
		ArrayList<Server> servers;
		servers = new ArrayList<Server>();
		
		try
		{
			for(int i = 0; i < args.length; ++i) {
				Registry reg = LocateRegistry.getRegistry(args[i]);
				Server serv = (Server) reg.lookup("Server");
				servers.add(serv);
			}
		}
		catch(RemoteException e)
		{
			e.printStackTrace();
			System.exit(1);
		}
		catch(NotBoundException e)
		{
			e.printStackTrace();
			System.exit(2);
		}

		if(servers.size() == 0) {
			System.out.println("Servers count is equal 0");
			System.exit(0);
		}
		
		
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in) );
		StringTokenizer cmdLine = null;
		String command = null;
		String servIndexS = null;
		int servIndex = 0;
		int tokens = 0;
		Server server = null;
		
		while(true) {
			try {
				cmdLine = new StringTokenizer(br.readLine());
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				System.exit(1);
			}
			
			if(cmdLine.countTokens() < 1)
				continue;
			
			servIndexS = cmdLine.nextToken();
			tokens = cmdLine.countTokens();
			
			if(servIndexS.equals("collect"))
			{
				File path = new File("");
				//File file;
				FileOutputStream out = null;
				try {
					//file = new File(path.getCanonicalPath() + "\\" + "registredData.txt");
					//file.createNewFile();
					//out = new FileOutputStream(file);
					
					RegistratorData data;
					if(servers.size() == 0) {
						System.out.println(("----------Cant find any servers----------"));
						//out.write(("----------Cant find any servers----------\r\n").getBytes());
					}
					else {
						for(int i = 0; i < servers.size(); ++i) {
							data = servers.get(i).getRegistredData();
							
							System.out.println(("----------Server: " + args[i] + "----------"));
							System.out.println(("kSearchNum:     " + data.getKSearchNum() + ""));
							System.out.println(("kSearchTimeout: " + data.getKSearchTimeout() + ""));
							System.out.println(("nodeNotFound:   " + data.getNodeNotFound() + ""));
							System.out.println(("sendNum:        " + data.getSendNum() + ""));
							System.out.println(("sendNumTimeout: " + data.getSendNumTimeout() + "\n"));
							
							/*out.write(("----------Server: " + args[i] + "----------\r\n").getBytes());
							out.write(("kSearchNum:     " + data.getKSearchNum() + "\r\n").getBytes());
							out.write(("kSearchTimeout: " + data.getKSearchTimeout() + "\r\n").getBytes());
							out.write(("nodeNotFound:   " + data.getNodeNotFound() + "\r\n").getBytes());
							out.write(("sendNum:        " + data.getSendNum() + "\r\n").getBytes());
							out.write(("sendNumTimeout: " + data.getSendNumTimeout() + "\r\n\r\n").getBytes());
							*/
						}
					}
					
				/*} catch (FileNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();*/
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if(out != null) {
					try {
						out.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				continue;
			}
			else if(servIndexS.equals("test"))
			{
				String i = cmdLine.nextToken();
				if(i == null)
					System.out.println("test null");
				else
					NodeId.encodeId(i);
				continue;
			}
			else if(servIndexS.equals("exit"))
			{
				System.out.println("exiting...");
				break;
			}
			else if(!isNumber(servIndexS)) {
				System.out.println("Error by parsing serverIndex");
				continue;
			}
			
			servIndex = Integer.parseInt(servIndexS);
			if(servIndex < 0 || servIndex >= servers.size()) {
				System.out.println("Incorrect index of server");
				continue;
			}
			server = servers.get(servIndex);
			
			command = cmdLine.nextToken().toLowerCase();
			if(command.equals("newnet")) {
				try {
					server.createNewNet();
					System.out.println("New net created");
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SocketException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NodeStateException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else if(command.equals("add")) {
				if(tokens < 3) {
					System.out.println("Usage: add <numberOfNodes> <starting port>");
					continue;
				}
				String numS = cmdLine.nextToken();
				if(!isNumber(numS)) {
					System.out.println("<numberOfNodes> (" + numS + ") must be number");
					continue;
				}
				int num = Integer.parseInt(numS);
				
				String portS = cmdLine.nextToken();
				if(!isNumber(portS)) {
					System.out.println("<starting port> (" + portS + ") must be number");
					continue;
				}
				int port = Integer.parseInt(portS);
				
				ArrayList<Pair<NodeId, Integer>> nodes = new ArrayList<Pair<NodeId, Integer>>(num);
				for(int i = 0; i < num; ++i) {
					nodes.add(new Pair<NodeId,Integer>(new NodeId(NodeId.encodeId(String.valueOf(i)))/*NodeId.random()*/, ++port));
				}
				
				try {
					server.addNodes(nodes);
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else if(command.equals("joinall")) {
				if(tokens < 3) {
					System.out.println("Usage: joinAll <address> <port>");
					continue;
				}
				
				String addressS = cmdLine.nextToken();
				InetAddress address;
				try {
					address = InetAddress.getByName(addressS);
				} catch (UnknownHostException e1) {
					// TODO Auto-generated catch block
					System.out.println("Unknown Host");
					e1.printStackTrace();
					continue;
				}
				
				String portS = cmdLine.nextToken();
				if(!isNumber(portS)) {
					System.out.println("<port> (" + portS + ") must be number");
					continue;
				}
				int port = Integer.parseInt(portS);

				try {
					server.joinAll(address, port);
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NodeStateException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (SocketException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (NodeConnectionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else if(command.equals("disconnectall")) {
				try {
					server.disconnectAll();
					System.out.println("All nodes disconnected");
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else if(command.equals("remove")) {
				if(tokens < 2) {
					System.out.println("Usage: remove <numberOfNodes>");
					continue;
				}
				String numS = cmdLine.nextToken();
				if(!isNumber(numS)) {
					System.out.println("<numberOfNodes> (" + numS + ") must be number");
					continue;
				}
				int num = Integer.parseInt(numS);
				
				try {
					server.removeNodes(num);
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ServerRemoveCountException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			else if(command.equals("send")) {
				if(tokens != 2)	{
					System.out.println("Usage: send <message>");
					continue;
				}
				String messageS = cmdLine.nextToken();
				Message message = new Message(messageS.getBytes());
				
				System.out.println("Sending: <" + messageS + ">");
				
				try {
					server.sendMessage(message);
				}
				catch(RemoteException e) {
					e.printStackTrace();
				}
			}
			else if(command.equals("sendrand")) {
				if(tokens != 2)	{
					System.out.println("Usage: send <numberOfMessages>");
					continue;
				}
				
				String numS = cmdLine.nextToken();
				
				if(!isNumber(numS)) {
					System.out.println("<numberOfNodes> (" + numS + ") must be number");
					continue;
				}
				int num = Integer.parseInt(numS);
				
				
				System.out.println("Sending: <" + num + ">");
				
				try {
					server.sendRandom(num);
				}
				catch(RemoteException e) {
					e.printStackTrace();
				}
			}
			
			else {
				System.out.println("Unrecognized command: '" + command + "'\n");
			}
		}
	}
	
	public static boolean isNumber(String value) {
		for(int i = 0; i < value.length(); ++i) {
			if(!Character.isDigit(value.charAt(i)))
				return false;
		}
		return true;
	}
}
